///////////////////////////////////////////////////////////////////////////////
// Company: University of Cincinnati
// Author: Jordan Ross, Ben Gentry, Bryan Butsch
//
// Created Date: 09/18/2013
// Design Name:
// Module Name: Proc
// Project Name: Proc
// Target Devices: Cyclone II EP2C20F484C7
// Tool Versions: Quartus 13.0
// Description: This module is a simple processor that supports 4 basic instructions.
//   The 4 instructions are as follows:
//   Operation             | Function Performed
//   ----------------------|-------------------------------
//   MV   Reg[X],Reg[Y]    | Reg[X] <-- {Reg[Y]}
//   MVI  Reg[X],#D        | Reg[X] <-- D
//   ADD  Reg[X], Reg[Y]   | Reg[X] <-- {Reg[X]} + {Reg[Y]}
//   SUB  Reg[X], Reg[Y]   | Reg[X] <-- {Reg[X]} - {Reg[Y]} 
//   LD   Reg[X], Reg[Y]   | Reg[X] <-- MEM[Reg[Y]]
//   ST   Reg[X], Reg[Y]   | MEM[Reg[Y]] <-- Reg[X]
//   MVNZ Reg[X], Reg[Y]   | if G!=0, Reg[X] <-- Reg[Y]
//
//   The inputs to the module are the data in (DIN) line, the processor clock,
//   Run, and active low reset. The outputs of this simple processor are the Bus,
//   a done signal, a data out port, and an address port.
// 
// Dependencies: regn.v
//               dec3to8.v
//               TenToOneMux.v
//               AddSub.v
//               pc.v
//               seg7_scroll.v
// 
// Revision:
// 0.01 - File Created
// 0.02 - Code compiled
// 0.03 - Added MV instruction. Compiles but not tested.
// 0.04 - Fixed next state not becoming the current state. Compiles correctly
// 0.05 - Added 9-bit Register declarations
// 0.06 - Fixed the mux. Compiles. Not tested.
// 0.06 - Fair amount of rework to the code. Added comments and MV instruction
//        no longer working because states are not changing.
// 0.07 - MV works perfectly. Changed MV to only require T1 to complete.
// 0.08 - AddSub module frame included.
// 0.09 - Addition framework in place, merge with MVI changes
// 0.10 - Added SUB instruction.
// 0.11 - Added SUB to case. ADD also added because identical to SUB in implementation
// 0.12 - Fixed ADD and SUB. Processor passes initial testing using TCL scripts.
// 0.13 - Minor code clean up. Processor passes final test bench test.
// 0.14 - Major changes. Load, Store, MVNZ, MVI, MV, ADD, and SUB all work and have
//        been tested using TCL scripts and memory. ADD, MVI, MV, and SUB have
//        been tested using the test bench.
// 0.15 - Added in Seven Segment display I/O to monitor registers R0, R1, R2, R3 
//
// Additional Comments:
//
///////////////////////////////////////////////////////////////////////////////
module Proc (DIN, Resetn, Clock, Run, Done, W, DOUT, ADDR, Seg0, Seg1, Seg2, Seg3);
    input [8:0] DIN;
    input Resetn, Clock, Run;
    output reg Done;
    output W;
    output [8:0] DOUT, ADDR;
    output [6:0] Seg0, Seg1, Seg2, Seg3;
    
    // Internal wire connecting the AddSub module to the G register
    wire [8:0] AddSubOut;
    
    // Timing cycles
    parameter T0 = 4'b0000, T1 = 4'b0001, T2 = 4'b0010, T3 = 4'b0011, T4 = 4'b0100, T5 = 4'b0101, T6 = 4'b0110, T7 = 4'b0111, T8 = 4'b1000;
    
    // Instructions
    parameter MV = 3'b000, MVI = 3'b001, ADD = 3'b010, SUB = 3'b011, LD = 3'b100, ST = 3'b101, MVNZ = 3'b110;
    
    // Select line for PC
    parameter PCout = 8'b00000001;
    
    // Next state, and current state values
    reg [2:0] Tstep_D, Tstep_Q;
    
    ////////////////////////////////////////////////////////
    // Instruction Format:
    // Bit:   | 8  |  7 |  6 |  5 |  4 |  3 |  2 |  1 |  0 |
    // Value: |I[2]|I[1]|I[0]|X[2]|X[1]|X[0]|Y[2]|Y[1]|Y[0]|
    ////////////////////////////////////////////////////////
    wire [8:0] IR, A, G, R0, R1, R2, R3, R4, R5, R6, PC, BusWires;
    wire [2:0] I;
    
    reg W_D;
    reg incr_pc;
    
    // 8 - bit representation of the 3-bit input given in the 
    // instruction.
    wire [7:0] Xreg, Yreg;
    
    // Select lines for the MUX
    reg Gout, DINout;
    reg [7:0] Regout;
    
    // Counter to keep the state in T7 for specified cycles
    reg counter_en;
    wire counter_done;
    
    // Control bits for the internal registers in the processor
    reg [12:0] Rin;     // Reset/load lines for registers 
                        // 0 -> IR
                        // 1 -> Ain
                        // 2 -> Gin
                        // 3-9 -> Reg_0-6
                        // 10 -> PC
                        // 11 -> DOUT
                        // 12 -> ADDR
    
    // An Error bit that is set if something is go wrong.
    // Maybe make useful later.
    //reg [1:0] Error;

    // Initialize the reg values
    initial begin
        Tstep_D = T0;
        Tstep_Q = T0;
        Gout = 0;
        DINout = 0;
        Regout = 'd0;
        Rin = 0;
        Done = 0;
        W_D = 0;
    end
    
    // Instantiate the instruction register as regn module
    regn ir(
        .R      (DIN), 
        .Rin    (Rin[0]), 
        .Clock  (Clock), 
        .Q      (IR),
        .Resetn (Resetn)
    );
    
    // Assign the top 3 bits of the IR as the instruction.
    assign I = IR[8:6];
    
    // Instantiate dec3to8 for the X and Y input
    dec3to8 decX (
        .W    (IR[5:3]),
        .En    (1'b1),
        .Y     (Xreg)
    );
    
    dec3to8 decY (
        .W    (IR[2:0]),
        .En    (1'b1),
        .Y     (Yreg)
    );
    
    // Instantiate the internal registers
    regn reg_A(
        .R        (BusWires),
        .Rin      (Rin[1]),
        .Clock    (Clock),
        .Q        (A),
		  .Resetn	(Resetn)
    );
    
    regn reg_G(
        .R        (AddSubOut),
        .Rin      (Rin[2]),
        .Clock    (Clock),
        .Q        (G),
		  .Resetn	(Resetn)
    );
    
    regn reg_0(
        .R        (BusWires),
        .Rin      (Rin[3]),
        .Clock    (Clock),
        .Q        (R0),
		  .Resetn	(Resetn)
    );
    
    regn reg_1(
        .R        (BusWires),
        .Rin      (Rin[4]),
        .Clock    (Clock),
        .Q        (R1),
		  .Resetn	(Resetn)
    );
    
    regn reg_2(
        .R        (BusWires),
        .Rin      (Rin[5]),
        .Clock    (Clock),
        .Q        (R2),
		  .Resetn	(Resetn)
    );
    
    regn reg_3(
        .R        (BusWires),
        .Rin      (Rin[6]),
        .Clock    (Clock),
        .Q        (R3),
		  .Resetn	(Resetn)
    );
    
    regn reg_4(
        .R        (BusWires),
        .Rin      (Rin[7]),
        .Clock    (Clock),
        .Q        (R4),
		  .Resetn	(Resetn)
    );
    
    regn reg_5(
        .R        (BusWires),
        .Rin      (Rin[8]),
        .Clock    (Clock),
        .Q        (R5),
		  .Resetn	(Resetn)
    );
    
    regn reg_6(
        .R        (BusWires),
        .Rin      (Rin[9]),
        .Clock    (Clock),
        .Q        (R6),
		  .Resetn	(Resetn)
    );
    
    pc pc(
        .R        (BusWires),
        .Incr     (incr_pc),
        .Rin      (Rin[10]),
        .Clock    (Clock),
        .Q        (PC)
    );
    
    regn reg_DOUT(
        .R        (BusWires),
        .Rin      (Rin[11]),
        .Clock    (Clock),
        .Q        (DOUT),
		  .Resetn	(Resetn)
    );
    
    regn reg_ADDR(
        .R        (BusWires),
        .Rin      (Rin[12]),
        .Clock    (Clock),
        .Q        (ADDR),
		  .Resetn	(Resetn)
    );
    
    regn #1 reg_W(
        .R        (W_D),
        .Rin      (1'b1),
        .Clock    (Clock),
        .Q        (W),
		  .Resetn	(Resetn)
    );
    
    // Instantiate the ten input mux that is controlled by 3 select lines (Gout,
    // DINout, and Regout[7:0]).
    TenToOneMux mux(Gout, DINout, Regout, R0, R1, R2, R3, R4, R5, R6, PC, DIN, G, BusWires);
    
    // Instantiate the AddSub module to perform the addition and subtraction operations
    // based on the state of I
    AddSub addsub(A, BusWires, I, AddSubOut);
    
    // Instantiate the seven segment scroll that will monitor registers R0, R1, R2, and R3.
    seg7_scroll sevenSeg(1'b1, R0, R1, R2, R3, Seg0, Seg1, Seg2, Seg3, Resetn);
    
    // Instatiate the counter that occurs at the seventh state
    t7counter T7Counter(counter_en, Clock, counter_done);
    
    // Control FSM state table
    always @(Tstep_Q, Run, I, counter_done) begin
        case (Tstep_Q)
            T0: // data is loaded into IR in this time step
                if (!Run) Tstep_D = T0; // If we have not started just stay at T0
                else Tstep_D = T1;      // If we have started continue on to T1
            T1:
                /*
                Forcing every instruction to 5 timecycles ( T0-T4 ) for simplicity
               
                if (I == MV || I == MVI) // We are done handling MV and MVI
                    Tstep_D = T0;
                else                     // Continue on for ADD and SUB
                    Tstep_D = T2;
                    
                */
                Tstep_D = T2;
            T2:
                Tstep_D = T3;
            T3:
                // Move to the next timing cycle
                Tstep_D = T4;
            T4: 
                Tstep_D = T5;
            T5:
                Tstep_D = T6;
            T6:
                Tstep_D = T7;
            T7:
                Tstep_D = T8;
            T8: // Cycle back to the start
                begin
                    counter_en = 1;
                    if (counter_done == 1) begin
                        counter_en = 0;
                        Tstep_D = T0;
                    end
                    else
                        Tstep_D = T8;
                end
            default : Tstep_D = T0; //Error = 'b01; // If an undefined timing, set the Error
        endcase
    end
    
    // Control FSM outputs
    always @(Tstep_Q or I or Xreg or Yreg or IR) begin
        // Specify initial values
        //Error = 0;
        DINout <= 0;
        Gout <= 0;
        Regout <= 0;
        Rin <= 0;
        incr_pc <= 0;
        W_D = 0;
        case (Tstep_Q)
            T0: // Load the next instruction addr from PC, send to memory
            begin
                Regout <= PCout; // Output the program counter to the BusWires so it can be loaded into ADDR.
                Rin[12] <= 1; // ADDRout
                Done = 0;  // Reset the Done output bit
            end
            T1: 
            begin
                // Wait for memory to read
                Done =0;
            end
            T2:
            begin
                // Wait for memory to read
                Done=0;
            end
            T3:  // Store DIN from memory in IR
            /*
            Note: This step assumes the read from memory happens in one time cycle, we may have
            to add additional time steps to accommodate the read from memory
            */
            begin
                Rin[0] <= 1;  // Set IRin to 1 so that we can load the instruction from DIN
                incr_pc <= 1;  // Increment PC
                Done = 0;
            end
            T4: // Define signals in time step 2
            begin
             // Set IRin & incr_pc to 0 so that we don't get a new value in the IR.
                 Rin[0] <= 0;
                 case (I)
                 /* Move the contents of Reg[Y] --> Reg[X] */
                 MV :
                     begin
                         Regout <= Yreg;     // Select Reg[Y] from the MUX so it
                                             // it is on the bus and can be loaded
                                             // into X.
                         Rin[(IR[5:3]+3)] <= 1; // Set Reg[X] to load input
                         Done = 1; //Set the done bit
                     end
                 /* Move an 9-bit constant into Reg[X] */
                 MVI :
                     begin
                         Rin[0] <= 0; // Retain the IR
                         // If we are doing an MVI we want to get our immediate from memory.
                         Regout <= PCout;
                         Rin[12] <= 1;
                         Done = 0;
                     end
                 /* Add/Subtract Reg[X] +/- Reg[Y] --> Reg[X] */
                 ADD, SUB:
                     begin
                         Regout <= Xreg; // Select Reg[X] from mux.                            
                         Rin[1] <= 1; // Move the contents of Reg[X] to Ain
                         Done = 0;
                     end
                 /* Loads data from memory address in Reg[Y] and stores it in Reg[X] */
                 LD:
                     begin
                         Regout <= Yreg; // Select Reg[Y] from mux.
                         //W_D = 1; // enable writing to memory
                         Rin[12] <= 1; // Move address contents of Reg[Y] to ADDR
                         Done = 0;
                     end
                 /* Store data from Reg[X] to memory address specified in Reg[Y] */
                 ST:
                     begin
                         Regout <= Xreg; // Select Reg[X] from mux.
                         Rin[11] <= 1; // Move data contents of Reg[X] to DOUT
                         Done = 0;
                     end
                 /* Move the contents of Reg[Y] --> Reg[X] if the contents of Reg[G] is not zero */
                 MVNZ:
                     begin
                         if (G != 0) begin
                             /* Execute MV instruction */
                             Regout <= Yreg;     // Select Reg[Y] from the MUX so it
                                                 // it is on the bus and can be loaded
                                                 // into X.
                             Rin[(IR[5:3]+3)] <= 1; // Set Reg[X] to load input
                         end
                         Done = 1;
                      end
                 default : Done = 1; //Error = 'b10; // If an undefined instruction, set the Error
                 endcase
             end
            T5: // Define signals in time step 2
             begin
                 case (I)
                 MV:
                     begin
                         Done = 0;
                     end                  
                 MVI:
                    begin
                        incr_pc <= 1;
                        Done = 0;
                    end
                ADD, SUB:
                     begin
                         Rin[1] <= 0; // Unset the A register to accept a new value;
                         Regout <= Yreg; // Select the register specified by the Y values in IR
                         Rin[2] <= 1; // Move the result of the addition to G
                         Done = 0;
                     end
                 LD:
                     begin
                         // Wait for memory
                         Done = 0;
                     end
                 ST:
                     begin
                            
                         Regout <= Yreg; // Select Reg[Y] from mux.
                         Rin[12] <= 1; // Move address contents of Reg[Y] to ADDR
                         W_D <= 1; // Enable write
                         Done = 0;
                     end
                 MVNZ:
                    begin
                        Done = 0;
                    end
                 default : Done = 1; //Error = 'b10; // If an undefined instruction, set the Error
                 endcase
             end
            T6: // Define signals in time step 3
                begin
                    case (I)
                    MV, MVI:
                         begin
                             Done = 0;
                         end
                    ADD, SUB:
                        begin
                            Rin[2] <= 0; // Unset the G register to accept a new value
                            Rin[(IR[5:3]+3)] <= 1; // Put the contents of Gout into R[X].
                            Gout <= 1; // Select Gout from mux.
                            Done = 1;
                        end
                    LD:
                        begin
                            // Wait for memory
                            Done = 0;
                        end
                    ST:
                        begin
                            W_D <= 0;
                            Done = 0;
                        end
                    default : Done = 1; //Error = 'b10; // If an undefined instruction, set the Error
                    endcase
                end
            T7:
                begin
                    case (I)
                    MV, ADD, SUB:
                        begin
                            Done = 0;
                        end
                    MVI:
                        begin
                            DINout <= 1; // Select DIN to be put onto the Bus
                            Rin[(IR[5:3]+3)] <= 1; // Set Reg[X] to load input
                            Done = 1;
                        end
                    ST:
                        begin
                            Done = 1;
                        end
                    LD:
                        begin
                            DINout <= 1;  // We want to get the data we read to memory onto the bus
                            Rin[(IR[5:3]+3)] <= 1; // Set Reg[X] to load from bus
                            Done = 1;
                        end
                    default : Done = 1;
                    endcase
                end
            T8:
                Done = 0;
            default : Done = 1; //Error = 1; // If an undefined timing cycle set the Error
        endcase
    end
    
    // Control FSM flip-flops
    always @(posedge Clock, negedge Resetn) begin
        if (!Resetn)
            Tstep_Q <= T0;      // Reset has been pressed, return back to start
        else
            Tstep_Q <= Tstep_D; // If reset has not been pressed then go to the next
                                // state.
    end
    
    // Assign the error LED to the Error bit
    //assign ErrorLED[1:0] = Error[1:0];
    
    // Assign the bus to the bus wires
    //assign Bus[8:0] = BusWires[8:0];
        
endmodule
